///////////////////////////////////////////////////////////
///////////////// Have an itch? Scratch it! ///////////////
///////////////////////// SCRATCH /////////////////////////
/////////////////////  A MUD  Server   ////////////////////
///////////////////// By: Jared Devall ////////////////////
/////////////////////      Thanks:     ////////////////////
/////////////////////  DIKU/Merc/ROM   ////////////////////
///////////////////// Aetas/Deus Gang  ////////////////////
/////////////////////       Beej       ////////////////////
///////////////////////////////////////////////////////////

///// If you see (WIN32), it is for Windows ONLY
///////////////////////////////////////////////////////////
#if defined (WIN32)
#include <winsock.h>

#define EWOULDBLOCK EAGAIN

#else
#define closesocket close

#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif                    

///// These are general includes, they work on any platform
///////////////////////////////////////////////////////////
#include <string>
#include <iostream> // The STL include.
#include <cstdarg>
#include <cstdio>
#include <cerrno>
#include <fcntl.h>
#include "socket.h"
#include <time.h>

#define BACKLOG 15

const int defaultPort = 2950; // If no port is specified at startup, we use this
using namespace std; 

Socket::Socket( ) {
	_disconnected = false;
	_gotInput = false;
}

Socket::~Socket( ){
	SocketServer::Instance()._socketList.remove( this );
	SocketServer::Instance().KillSocket( this ); 
}

int Socket::GetDescriptor( ) {
	return _sockfd;
}

void Socket::SetDescriptor( int desc ) {
	_sockfd = desc;
}

string Socket::GetSocketIP( ) {
	return _ipAddress;
}

void Socket::SetSocketIP( const string &ip ) {
	_ipAddress = ip;
}

string Socket::GetSocketHost( ) {
	return _host;
}

void Socket::SetSocketHost( const string &host ) {
	_host = host;
}

void Socket::SetDisconnected( bool value ) {
	_disconnected = value;
}

bool Socket::IsDisconnected( ) {
	return _disconnected;
}

void Socket::SetGotInput( bool value ) {
	_gotInput = value;
}

bool Socket::GotInput( ) {
	return _gotInput;
}

string Socket::GetInput( ) {
	return _input;
}

string Socket::GetOutput( ) {
	return _output;
}

void Socket::FlushOutput( ) {
	_output.erase();
}

void Socket::FlushInput( ) {
	_gotInput = false;
	_input.erase();
}

void Socket::SetInput( const string &input ) {
	_input = input;
}

void Socket::SetOutput( const string &output ) {
	_output = output;
}

void Socket::Send( char * message, ... ) {
	va_list args;
	char buf[MAX_BUFFER];
	string buf2;

    va_start( args, message );

	vsprintf( buf, message, args);

	buf2 = buf;

	_output.append( buf2 );

	va_end( args );
}

void Socket::Send( const std::string &message ) {
	_output.append( message );
}


////////////////////////////// socket server /////////////////
SocketServer::SocketServer( ) {
	_defaultPort = defaultPort;
	_newConnection = false;
	
#if defined WIN32
	InitWinsock(); // Get our Sockets up and Running.
#endif
}

SocketServer::~SocketServer( ) {
#if defined WIN32
	DeInitWinsock(); // Shutdown winsock
	 
#endif
}

#if defined WIN32 
// Initializes Windows' Sockets
void SocketServer::InitWinsock( ) {
	WSADATA wsaData;   

	if ( WSAStartup( MAKEWORD( 1, 1 ), &wsaData ) != 0 ) { 
            fprintf( stderr, "WSAStartup failed.\n" ); 
            exit( 1 );
	}
}

void SocketServer::DeInitWinsock( ) {
	WSACleanup();
}
#endif

bool SocketServer::CreateSocket( ) {
	// Let's Initialize a socket for the server
	
	if ( ( _sockfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 ) { 
		perror( "CreateSocket::socket " ); 
		return false;
	}
	
	return true;
}

bool SocketServer::ReuseAddress( ) {
	int yes = 1;

	// Makes sure we can re-use the port if part of a socket is still lingering.
	// No "Address Already In Use" message.
	if ( setsockopt( _sockfd, SOL_SOCKET, SO_REUSEADDR, ( const char * )&yes, sizeof(int) ) == -1 ) {
        perror( "ReuseAddress::setsockopt " ); // Give us the error.
        return false;
    }
	return true;
}

// Binds server to specific port
bool SocketServer::BindSocket( int port ) {
	sockaddr_in my_addr;  	

	if ( ( port < 1024 ) || ( 65535 < port ) ) {
		cout << "Port " << port << " out of range.\n";
		cout << "Defaulting to port" << _defaultPort << endl;
		port = _defaultPort; // You can change this to default to whatever you want.
	}

	if ( !ReuseAddress() )
		return false;

	my_addr.sin_family = AF_INET;         // host byte order
    my_addr.sin_port = htons( port );     // short, network byte order
    my_addr.sin_addr.s_addr = htonl( INADDR_ANY ); // auto-fill with my IP
    memset( &( my_addr.sin_zero ), '\0', 8 ); // zero the rest of the struct

    // Bind the server to port so people can connect to a certain port.
	if ( ( bind( _sockfd, ( struct sockaddr * )&my_addr, sizeof( struct sockaddr ) ) ) == -1 ) {
		perror( "BindSocket::bind " ); 
		return false;
	}

	return true;
}

// Start listening on the port we binded to
bool SocketServer::ListenSocket( ) {
	// Start listening for connections and other stuff.
	if ( listen( _sockfd, BACKLOG ) == -1 ) {
	 	perror( "listenSocket::listen " );
		return false; 
	}
     
	return true;
}

// Disconnects a specific socket
void SocketServer::KillSocket( Socket * socket ) {	
	shutdown( socket->GetDescriptor(), 2 ); // A cleaner close.
	closesocket( socket->GetDescriptor() ); // Close the Socket
    return;
}

// Goes through the socket lists and kills ones that have disconnected
void SocketServer::KillDisconnectedSockets( ) {
	list< Socket * >::iterator sock;
	Socket *socket;

	for ( sock = _socketList.begin(); sock != _socketList.end(); ) {
		socket = (*sock);
		if ( socket->IsDisconnected() ) {			
			sock = _socketList.erase( sock );			
			KillSocket( socket );
			cout << socket->GetSocketIP() << " has disconnected.\n\r";
		} else {
			++sock;
		}		
	}	
} 

 // Creates new connection
void SocketServer::ConceiveConnection( ) {
	char buf[MAX_BUFFER];
	string buf2;
	//avatarMain * avatar;
	struct sockaddr_in sock;
	//struct hostent *   from;
	Socket *   socket;
	int sock_fd;
	int size;
	int addr;

	size = sizeof( struct sockaddr_in );

	// I don't know why it does this, but I have to have Windows use plain &size and
	// linux cast &size to (socklen_t *)
#if defined (WIN32)
	if ( ( sock_fd = accept( _sockfd, ( struct sockaddr* ) &sock, &size ) ) == -1 ) {
		perror( "ConceiveConnection::accept " );
		return;
	} 	
#else
    if ( ( sock_fd = accept( _sockfd, ( struct sockaddr* ) &sock, ( socklen_t* ) &size ) ) == -1 ) {
		perror( "ConceiveConnection::accept " );
		return;
	} 
#endif

	// This'll get the IP address. xxx.xxx.xxx.xxx 
	addr = ntohl( sock.sin_addr.s_addr );
	sprintf( buf, "%d.%d.%d.%d",
	    ( addr >> 24 ) & 0xFF, ( addr >> 16 ) & 0xFF,
	    ( addr >>  8 ) & 0xFF, ( addr       ) & 0xFF
	    );

	cout << "New connection from: " << buf << endl;


    
	// This was producing some problems when testing, will have to get back to thread it
	// FIXME
	/*	// This gets the 'name' of the host from it's address. i.e. bob@ ->jimmysinternet.com<-
	if ( ( from = gethostbyaddr( (char *) &sock.sin_addr, sizeof( sock.sin_addr ), AF_INET ) ) == NULL ) {
		cout << "System.error :: gethostbyaddr()" << endl;
	    return;
	}*/

	socket = new Socket;

    socket->SetDescriptor( sock_fd );
	socket->SetSocketIP( buf ); // host is just the normal IP
	//desc->setUserIPlong( from ? from->h_name : buf ); // This is the "long-host", or name one.

	_socketList.push_back( socket ); // We have to add it to the descriptor List
	
	_newConnection = true;
}

// does the main work..
void SocketServer::ChaperoneConnections( ) {
	int stuff;
    list< Socket * >::iterator sock;	 // Live Descriptors
	int high = _sockfd;

	//clockStart = clock();// Check the time when we start the looping

	// This Zeroes or "Cleans" these fd sets.
	FD_ZERO( &_fd_read );
	//FD_ZERO( &_fd_write );
	FD_ZERO( &_fd_exc );
	FD_SET( _sockfd, &_fd_read );

	// Checks for sockets already in the list
	for ( sock = _socketList.begin(); sock != _socketList.end(); ++sock ) {
		if ( SendOutput( (*sock) ) != true ) { // This sends stuff to connections.
			(*sock)->SetDisconnected( true );
		}
				if( (*sock)->GetDescriptor() > high )
			high = (*sock)->GetDescriptor();

		// This sets the descriptors in the fd sets.
		FD_SET( (*sock)->GetDescriptor(), &_fd_read );
		//FD_SET( (*sock)->GetDescriptor(), &_fd_write );
		FD_SET( (*sock)->GetDescriptor(), &_fd_exc );
	} 

	// select can be woken up by seeing a socket sending info
	// needing to recieve info, or throwing an exception.
	if ( select( high + 1, &_fd_read, NULL, &_fd_exc, 0 ) < 0 ){
		perror("select: ");
		return;
	}

	// Let's create a new socket.
	if ( FD_ISSET( _sockfd, &_fd_read ) ) {
		ConceiveConnection();
	}

	for ( sock = _socketList.begin(); sock != _socketList.end(); ++sock ) {
		// The socket had an accident. Let's kill it.
		if ( FD_ISSET( (*sock)->GetDescriptor(), &_fd_exc ) ) {
			(*sock)->SetDisconnected( true );	
			continue;
		}

		// Well, lets see what the socket can say!
		if( FD_ISSET( (*sock)->GetDescriptor(), &_fd_read ) ) {
			if ( (*sock)->GetDescriptor() == _sockfd ) {
                 ConceiveConnection(); // Make a new Connection
            } else { // This was an older connection.
				if ( ( stuff = RecieveInput( (*sock) ) ) == Disconnected ) { // Recieve Input from a Connection
					(*sock)->SetDisconnected( true );
					continue;
				} else if ( stuff == Complete ) {
					(*sock)->SetGotInput( true );
				}
			}
		}
	}
	
	KillDisconnectedSockets();
	return;
}

///////// Name: listenConnection
///////// Args: Socket Descriptor that's sending info to server
///////// Does: Recieves input from a connection
///////////////////////////////////////////////////////////////
int SocketServer::RecieveInput( Socket * socket ) {
	string::iterator cChar;
	string buf2;
	string input;
	long len = 0;
	long result = 0;
	char buf[MAX_BUFFER];
	buf[0] = '\0';
	strncpy( buf, "\0", sizeof( buf ) );
	input = socket->GetInput();

		if ( ( ( result = recv( socket->GetDescriptor(), buf + len, sizeof( buf ) - len, 0 ) ) <= 0  ) ) {
			if ( result == 0 ) {
				// nothing
			} else if ( errno == EWOULDBLOCK ) {
				input.append( buf );
				socket->SetInput( input );
				for ( cChar = input.begin(); cChar != input.end(); ++cChar ) {
					if ( (*cChar) == '\n' || (*cChar) == '\r' ) {
						return Complete;
					}
				}

				return Incomplete;
			} else {
				perror( "RecieveInput::recv " );
			}
			return Disconnected;
		}
		len += result;
		
    	// This removes a Backspace or Delete.
		if ( buf[len-1] == 8 || buf[len-1] == 127 ) {
			if ( strlen( buf ) > 0) {
				buf[len-1] = '\0';
				buf[len-2] = '\0';
				len -= 2;
			} else {
				buf[len-1] = '\0';
				len--;
			}
		}

  
	input.append( buf );
	socket->SetInput( input );
		for ( cChar = input.begin(); cChar != input.end(); ++cChar ) {
			if ( (*cChar) == '\n' || (*cChar) == '\r' ) {
				return Complete;
			}
		}
	return Incomplete;
}

////////// Name: sendConnection
////////// Args: Socket Descriptor to send info to.
////////// Does: Sends Output to a Connection who has stuff in 
//////////       their outbuf.
///////////////////////////////////////////////////////////////
bool SocketServer::SendOutput( Socket * socket ) {
	string buf;
	string buf2;

	if ( socket->GetOutput().empty() )
		return true;
	
    buf = socket->GetOutput();
	
	if ( send( socket->GetDescriptor(), buf.c_str(), buf.length(), 0 ) < 0 ) {
		perror( "SendOutput::send " );
		return false;
	}
    
    socket->FlushOutput();
	return true;
} 

int SocketServer::GetHostSocket( ) {
	return _sockfd;
}

void SocketServer::SetHostSocket( int sockfd ) {
	_sockfd = sockfd;
}

void SocketServer::Start( ) {
	CreateSocket( ); // Open up our socket.
	BindSocket( _defaultPort ); // bind the socket to the port.
	ListenSocket( ); // Begin listening	
}

void SocketServer::Start( int port ) {
	CreateSocket( ); // Open up our socket.
	BindSocket( port ); // bind the socket to the port.
	ListenSocket( ); // Begin listening	
}

bool SocketServer::Monitor ( ) {
	ChaperoneConnections();
	
	if ( _newConnection ) {
		_newConnection = false;
		return true;
	}
		
	return false;
}
